/**
 *
 */
package co.fitstart.payment.web.controller.alipay;

import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import com.alipay.api.response.AlipayTradeQueryResponse;
import com.demo2do.core.BusinessException;
import com.demo2do.core.support.Result;
import com.demo2do.core.utils.JsonUtils;
import com.demo2do.core.web.utils.WebUtils;

import co.fitstart.payment.Constant;
import co.fitstart.payment.alipay.pay.AlipayCommonParameter;
import co.fitstart.payment.alipay.pay.AlipayWapPayParameter;
import co.fitstart.payment.alipay.pay.handler.AlipayPayHandler;
import co.fitstart.payment.cache.accessor.PersistentAccessor;
import co.fitstart.payment.entity.app.App;
import co.fitstart.payment.entity.app.AppSetting;
import co.fitstart.payment.entity.channel.Channel;
import co.fitstart.payment.entity.request.RequestRecord;
import co.fitstart.payment.entity.transaction.TransactionRecord;
import co.fitstart.payment.entity.transaction.TransactionStatus;
import co.fitstart.payment.service.AppService;
import co.fitstart.payment.service.RequestRecordService;
import co.fitstart.payment.service.TransactionRecordService;
import co.fitstart.payment.web.handler.RequestValidateHandler;

/**
 * @author lute
 */
@Controller
@RequestMapping("/alipay")
public class AlipayController {

    private static final Logger logger = LoggerFactory.getLogger(AlipayController.class);

    @Value("#{ctx['site.host']}")
    private String host;

    @Autowired
    private AppService appService;

    @Autowired
    private RequestValidateHandler requestValidateHandler;

    @Autowired
    private PersistentAccessor accessor;

    @Autowired
    private RequestRecordService requestRecordService;

    @Autowired
    private AlipayPayHandler alipayPayHandler;

    @Autowired
    private TransactionRecordService transactionRecordService;

    /**
     *
     * @param appid
     * @param channelName
     * @param orderNo
     * @param customerId
     * @param appNotifyURL
     * @param signature
     * @param httpServletRequest
     * @return
     */
    @ResponseBody
    @RequestMapping("/wappay")
    public String requestWapPay(@RequestParam("appid") String appid,
                                @RequestParam("channel") String channelName,
                                @RequestParam("order_no") String orderNo,
                                @RequestParam("customer_id") String customerId,
                                @RequestParam("app_notify_url") String appNotifyURL,
                                @RequestParam("signature") String signature,
                                HttpServletRequest httpServletRequest) {

        Result result = new Result();

        // Get app and channel
        App app = appService.getApp(appid);
        Channel channel = accessor.getChannels().get(channelName);
        // Extract post request parameters
        Map<String, String> params = WebUtils.extractPostParameters(httpServletRequest);
        // Validate request parameters
        try {
            requestValidateHandler.validateRequestParameters(app, channel, params);
        } catch (BusinessException e) {
            logger.error("AlipayController#requestWapPay - Validate incoming request parameters fails: {}", e);
            return JsonUtils.toJsonString(result.fail().message(e.getMessage()));
        }

        // Create request record
        RequestRecord requestRecord = new RequestRecord(app, channel, orderNo, customerId, appNotifyURL);
        requestRecordService.create(requestRecord);

        // Get app settings
        Map<String, AppSetting> appSettings = accessor.getAppSettings().get(app.getId());
        String alipayAppid = appSettings.get(Constant.SETTING_ALI_APPID).getValue();
        String privateKey = appSettings.get(Constant.SETTING_ALI_PRIVATE_KEY).getValue();
        String publicKey = appSettings.get(Constant.SETTING_ALI_PUBLIC_KEY).getValue();
        String sellerId = appSettings.get(Constant.SETTING_ALI_SELLERID).getValue();

        AlipayCommonParameter commonParameter = new AlipayCommonParameter(alipayAppid, publicKey, privateKey);
        AlipayWapPayParameter wapPayParameter = constructAlipayWapPayParameter(requestRecord.getRequestNo(), sellerId, params);

        String form = alipayPayHandler.requestWapPay(commonParameter, wapPayParameter);
        if (StringUtils.isNotBlank(form)) {
            int amount = Integer.valueOf(params.get("total_amount"));
            TransactionRecord transactionRecord = new TransactionRecord(requestRecord, null, amount);
            transactionRecordService.createTransactionRecord(transactionRecord);
            result.success().data("form", form);
        } else {
            result.fail().message("Request wap pay fails.");
        }
        return JsonUtils.toJsonString(result);
    }

    /**
     * @param requestNo
     * @param sellerId
     * @param params
     * @return
     */
    private AlipayWapPayParameter constructAlipayWapPayParameter(String requestNo, String sellerId, Map<String, String> params) {
        String notifyURL = host + "/alipay/callback";
        String returnURL = params.get("return_url");
        String subject = params.get("subject");
        int totalAmount = Integer.valueOf(params.get("total_amount"));
        String productCode = params.get("product_code");

        AlipayWapPayParameter wapPayParameter = new AlipayWapPayParameter(notifyURL, returnURL, subject, requestNo, totalAmount, productCode);

        String body = params.get("body");
        if (StringUtils.isNotBlank(body)) {
            wapPayParameter.body(body);
        }
        String timeoutExpress = params.get("timeout_express");
        if (StringUtils.isNotBlank(timeoutExpress)) {
            wapPayParameter.timeoutExpress(timeoutExpress);
        }
        String authToken = params.get("auth_token");
        if (StringUtils.isNotBlank(authToken)) {
            wapPayParameter.authToken(authToken);
        }
        if (StringUtils.isNoneBlank(sellerId)) {
            wapPayParameter.sellerId(sellerId);
        }
        return wapPayParameter;
    }

    /**
     * @param appid
     * @param channelName
     * @param orderNo
     * @param signature
     * @param httpServletRequest
     * @return
     */
    @ResponseBody
    @RequestMapping(value = "/orderquery", method = RequestMethod.POST)
    public String queryOrder(@RequestParam("appid") String appid,
                             @RequestParam("channel") String channelName,
                             @RequestParam("order_no") String orderNo,
                             @RequestParam("signature") String signature,
                             HttpServletRequest httpServletRequest) {

        Result result = new Result();

        // Get app and channel
        App app = appService.getApp(appid);
        Channel channel = accessor.getChannels().get(channelName);
        // Extract post request parameters
        Map<String, String> params = WebUtils.extractPostParameters(httpServletRequest);
        // Validate request parameters
        try {
            requestValidateHandler.validateRequestParameters(app, channel, params);
        } catch (BusinessException e) {
            logger.error("AlipayController#requestWapPay - Validate incoming request parameters fails: {}", e);
            return JsonUtils.toJsonString(result.fail().message(e.getMessage()));
        }

        // Get app settings
        Map<String, AppSetting> appSettings = accessor.getAppSettings().get(app.getId());
        String alipayAppid = appSettings.get(Constant.SETTING_ALI_APPID).getValue();
        String privateKey = appSettings.get(Constant.SETTING_ALI_PRIVATE_KEY).getValue();
        String publicKey = appSettings.get(Constant.SETTING_ALI_PUBLIC_KEY).getValue();

        List<RequestRecord> requestRecords = requestRecordService.getRequestRecordsOrderedByDescendingTime(orderNo);
        for (RequestRecord requestRecord : requestRecords) {
            if (!requestRecord.isDisabled()) {
                String requestNo = requestRecord.getRequestNo();

                // Build common parameter
                AlipayCommonParameter commonParameter = new AlipayCommonParameter(alipayAppid, publicKey, privateKey);

                // Get transaction record by request number
                TransactionRecord transactionRecord = transactionRecordService.getTransactionRecord(requestNo);
                String tradeNo = null;
                if (transactionRecord == null) {
                    logger.warn("AlipayPayHandler#queryOrder() - Can not find transaction record with request number [{}].", requestNo);
                } else {
                    tradeNo = transactionRecord.getTradeNo();
                }

                // Execute query request
                AlipayTradeQueryResponse response = alipayPayHandler.queryOrder(commonParameter, requestNo, tradeNo);
                if (response != null) {
                    if (response.getTradeStatus().equals("TRADE_SUCCESS")) {
                        // Update transaction record if necessary
                        if (transactionRecord.getTransactionStatus().isCreated()) {
                            String alipayTradeNo = response.getTradeNo();
                            transactionRecordService.changeTransactionStatus(transactionRecord, TransactionStatus.PAID, alipayTradeNo);
                            // Get transaction record after status update
                            transactionRecord = transactionRecordService.getTransactionRecord(requestNo);
                        }
                        // Return immediately if queried a trade success order
                        result.success().data("appid", transactionRecord.getAppId())
                                        .data("channel_id", transactionRecord.getChannelId())
                                        .data("amount", transactionRecord.getAmount())
                                        .data("order_no", transactionRecord.getOrderNo())
                                        .data("request_no", transactionRecord.getRequestNo())
                                        .data("trade_no", transactionRecord.getTradeNo())
                                        .data("transaction_status", transactionRecord.getTransactionStatus());
                        return JsonUtils.toJsonString(result);
                    } else {
                        String tradeStatus = response.getTradeStatus();
                        Long id = transactionRecord.getId();
                        TransactionStatus status = transactionRecord.getTransactionStatus();
                        logger.trace("WechatController#queryOrder - Current transaction record [{}] has status [{}] and recieves trade state [{}]", id, status, tradeStatus);
                    }
                }
            }
        }
        return JsonUtils.toJsonString(result.fail());
    }

}
